package com.gmrz.webauthn.db.dao;

import com.gmrz.uaf.db.DAOException;
import com.gmrz.util.db.DBUtil;
import com.gmrz.webauthn.protocol.v1.processor.WebAuthnErrorCode;
import com.gmrz.webauthn.protocol.v1.schema.WebAuthnMetadata;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class BaseWebAuthnMetadataDAO extends Dialect implements WebAuthnMetadataDAO {

    private static final Logger LOG = LogManager.getLogger(BaseWebAuthnPolicyDAO.class);

    public static final String _SQL_SELECT_ALL = "select metadata_id,aaid,auth_version,description,json_data,status,createts,updatets,auth_type from t_auth_metadata where protocol='fido2' ";

    private Connection conn_;

    @AssistedInject
    public BaseWebAuthnMetadataDAO(@Assisted Connection conn_) {
        this.conn_ = conn_;
    }

    @Override
    public WebAuthnMetadata findByAAGUID(String aaguid) throws DAOException {
        String sql = _SQL_SELECT_ALL + " and aaid = ? ";
        PreparedStatement s = null;
        ResultSet rs = null;

        WebAuthnMetadata webAuthnMetadata = null;
        try {
            s = this.conn_.prepareStatement(sql);
            s.setString(1, aaguid);

            rs = s.executeQuery();

            while (rs.next()) {
                webAuthnMetadata = readResultSet(rs);
            }
        } catch (SQLException sqle) {
            LOG.error("The following SQL statement failed to execute:\n {}", sql);
            throw new DAOException(WebAuthnErrorCode.ERROR_DATABASE_READ_ERROR, sqle);
        } finally {
            DBUtil.close(rs);
            DBUtil.close(s);
        }

        return webAuthnMetadata;
    }


    @Override
    public List<WebAuthnMetadata> findMetadataByPolicyID(String policyId) throws DAOException {
        String sql = "select b.metadata_id,b.aaid,b.auth_version,b.description,b.json_data,b.status,b.createts,b.updatets,b.auth_type from t_policy_auth a left join t_auth_metadata b on a.metadata_id = b.metadata_id where a.pl_id = ?";
        PreparedStatement s = null;
        ResultSet rs = null;
        List<WebAuthnMetadata> webAuthnMetadataList = new ArrayList<WebAuthnMetadata>();
        try {
            s = this.conn_.prepareStatement(sql);
            s.setString(1, policyId);

            rs = s.executeQuery();

            while (rs.next()) {
                webAuthnMetadataList.add(readResultSet(rs));

            }
        } catch (SQLException sqle) {
            LOG.error("The following SQL statement failed to execute:\n {}", sql);
            throw new DAOException(WebAuthnErrorCode.ERROR_DATABASE_READ_ERROR, sqle);
        } finally {
            DBUtil.close(rs);
            DBUtil.close(s);
        }

        return webAuthnMetadataList;
    }

    @Override
    public Set<String> findAaguidArr(String tenantId, String authType, String transType) throws DAOException {
        String sql = "select t_auth_metadata.aaid from t_policy_auth left join t_auth_metadata on t_policy_auth.metadata_id = t_auth_metadata.metadata_id " +
                "where t_policy_auth.pl_id = (select pl_id from t_policy where tenant_id = ? and auth_type = ? and trans_type = ?)";
        PreparedStatement s = null;
        ResultSet rs = null;
        Set<String> result = new HashSet<String>();

        try {
            s = this.conn_.prepareStatement(sql);

            int offset = 1;
            s.setString(offset++, tenantId);
            s.setString(offset++, authType);
            s.setString(offset, transType);

            rs = s.executeQuery();

            while (rs.next()) {
                result.add(rs.getString(1));
            }

        } catch (SQLException sqle) {
            LOG.error("The following SQL statement failed to execute:\n {}", sql);
            throw new DAOException(WebAuthnErrorCode.ERROR_DATABASE_READ_ERROR, sqle);
        } finally {
            DBUtil.close(rs);
            DBUtil.close(s);
        }
        return result;
    }

    private WebAuthnMetadata readResultSet(ResultSet rs) throws DAOException {
        WebAuthnMetadata webAuthnMetadata = new WebAuthnMetadata();
        try {
            int offset = 1;
            webAuthnMetadata.setWmId(rs.getString(offset++));
            webAuthnMetadata.setAaguiid(rs.getString(offset++));
            webAuthnMetadata.setAuthVersion(rs.getString(offset++));
            webAuthnMetadata.setDescription(rs.getString(offset++));
            webAuthnMetadata.setJsonData(rs.getString(offset++));
            webAuthnMetadata.setStatus(rs.getInt(offset++));
            webAuthnMetadata.setCreatets(rs.getTimestamp(offset++));
            webAuthnMetadata.setUpdatets(rs.getTimestamp(offset++));
            webAuthnMetadata.setAuthType(rs.getString(offset));

            return webAuthnMetadata;

        } catch (SQLException sqle) {
            LOG.error("Failed to read the Tenant Config Data", sqle);
            throw new DAOException(WebAuthnErrorCode.ERROR_DATABASE_READ_ERROR, sqle);
        }
    }

}
